/* libstdf -- library for reading writing and manipulating STDF files and data.
   Copyright (C) 2009 Tushar Jog
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General
   Public License along with this library; if not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA 
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Data;

using stdf;

using U1 = System.Byte;
//using C1 = System.SByte;
using C1 = System.Char;
using B1 = System.Byte;
using N1 = System.Byte;
using I1 = System.SByte;

using U2 = System.UInt16;
using I2 = System.Int16;

using U4 = System.UInt32;
using I4 = System.Int32;
using R4 = System.Single;

using R8 = System.Double;

//
// TODO : in case validHeader is not found, print the previous Record, 
//         since it is most likely that the previous record had some issue
//

namespace stdf {
	
	public class StdfFile {
		public StdfFile() {
			stdfStream = new Stream();
			
		}
		public StdfFile(string name, RWMode mode) {
			fileName = name;
			rwMode = mode;
			stdfStream = new Stream(fileName, rwMode);
		}
		
		public bool loadMIR = true;
		public bool loadSDR = true;
        public bool loadWIR = true;
        public bool loadPRR = true;
        public bool loadPTR = true;
        public bool loadFTR = true;
        public bool loadMPR = false;

        public bool ignoreAlarm = true;

        public virtual bool open( string name, RWMode mode) {
            bool success = false;

            success = stdfStream.open( name, mode);

            if(success == true) {
                fileName = name;
                rwMode   = mode;
            }

            return success;
        }

        private bool addColumn(ref DataTable data1, ref DataTable data2, string name, Type type)
        {
            bool result = true;

            data1.Columns.Add(name, type);
            data2.Columns.Add(name, type);

            return result;
        }

        public virtual bool read( ref DataTable data, ref DataTable headerData) {
            bool result = false;

            //
            //  Local store of variables and containers
            //
            RecordHeader header = new RecordHeader();
            FARRecord    far    = new FARRecord();
            MIRRecord    mir    = new MIRRecord();
            SDRRecord    sdr    = new SDRRecord();
            WIRRecord    wir    = new WIRRecord();
            List< Pair<string, string> > mirFields = new List<Pair<string, string>>();
            List<Pair<string, string>>   sdrFields = new List<Pair<string, string>>();
            List<Pair<string, string>>   wirFields = new List<Pair<string, string>>();
            // Keep a list/vec or all header records
            List<ATRRecord> atrVec = new List<ATRRecord>();
            List<SDRRecord> sdrVec = new List<SDRRecord>();
            List<PLRRecord> plrVec = new List<PLRRecord>();
            List<PGRRecord> pgrVec = new List<PGRRecord>();
      		List<PMRRecord> pmrVec = new List<PMRRecord>();
      		
            //
            //  Local flags
            //
            bool validRecord  = true;
            bool foundMIR     = false;
            bool foundSDR     = false;
            bool foundPIR     = false;
            bool foundWIR     = false;
            bool foundSummary = false;

            //
            // Read FAR record and set byte order
            //
            stdfStream.readRecordHeader( ref header);
            stdfStream.readRecord( ref far);
            stdfStream.setByteOrder(far.getByteOrder());
            //Debug.WriteLine( header.ToString());
            //Debug.WriteLine( far.ToString());

            if(far.stdfVersion == 3)
            	return false; // Cannot handle ver 3.0
            
            //
            // Now that the stdf file has been opened, it is OK to clear the data container
            //
            data.Clear();

            List<string> columnHeader;

            Dictionary<string, int>         columnName2Index = new Dictionary<string, int>();
            string firstParametricColumnName = "";
            string firstFunctionalColumnName = "";
			Dictionary<U1, int> siteNum2RowIndex = new Dictionary<U1, int>();
			Dictionary<U4, PTRRecord> ptrRegister = new Dictionary<uint, PTRRecord>();
			Dictionary<U4, FTRRecord> ftrRegister = new Dictionary<uint, FTRRecord>();
			
            DataTable touchDownResults = new DataTable();
            DataTable touchDownPartInfo = new DataTable();
            
            uint uniqueTestCount = 0;
            uint uniquePartNumber = 0;

            while(stdfStream.isReadable()) {
                stdfStream.readRecordHeader(ref header);
                //Debug.WriteLine( header.ToString());
                //stdfStream.skip(header.getLength());
                
                RecordType recType = header.getRecordType();
                
                //if(recType == RecordType.MIR) foundMIR = true; // there can only be one
                if(recType == RecordType.PIR) foundPIR = true; // Header info is over, data begins
                if(recType == RecordType.TSR) foundSummary = true; // Data info is over, summary begins
                
                int recReadLen = 0;
                uint expectedLen = (uint) header.getLength();
                
                if(foundMIR == false) {
                	// Valid records are ATR MIR
                	if(recType == RecordType.ATR) {
                		ATRRecord atr = new ATRRecord();
                		recReadLen = stdfStream.readRecord(ref atr, expectedLen);
                		atrVec.Add(atr);
                		//Debug.WriteLine(atr.ToString());
                	}
                	else if(recType == RecordType.MIR) {
                		recReadLen = stdfStream.readRecord( ref mir, expectedLen);
                		foundMIR = true;
                		//Debug.WriteLine(mir.ToString());

                        // Create STDF Filename column
                        //touchDownResults.Columns.Add("Filename", typeof(string));
                        addColumn(ref touchDownResults, ref data, "Filename", typeof(string));
                        if (loadMIR)
                        {
                            // Create MIR fields columns
                            mir.getFields(ref mirFields);
                            for (int i = 0; i < mirFields.Count; i++)
                            {
                                //touchDownResults.Columns.Add( mir.getRecordTypeString() + "." + mirFields[i].First, typeof(string));
                                addColumn(ref touchDownResults, ref data, mir.getRecordTypeString() + "." + mirFields[i].First, typeof(string));
                            }
                        }
                	}
                	else {
                		validRecord = false;
                	}
                }
                else if((foundMIR == true) && (foundPIR == false)) {
                	// Valid records are SDR, PLR, PGR, PMR
                	if(recType == RecordType.SDR) {
                		//SDRRecord sdr = new SDRRecord();
                		recReadLen = stdfStream.readRecord( ref sdr, expectedLen);
                		//Debug.WriteLine( sdr.ToString());
                        if (foundSDR == false) // Protect against multiple SDR records
                        {
                            if (loadSDR)
                            {
                                sdr.getFields(ref sdrFields);
                                for (int i = 4; i < sdrFields.Count; i++)
                                {
                                    //touchDownResults.Columns.Add(sdr.getRecordTypeString() + "." + sdrFields[i].First, typeof(string));
                                    addColumn(ref touchDownResults, ref data, sdr.getRecordTypeString() + "." + sdrFields[i].First, typeof(string));
                                }
                            }
                        }
                        foundSDR = true;
                	}
                	else if(recType == RecordType.PMR) {
                		PMRRecord pmr = new PMRRecord();
                		recReadLen = stdfStream.readRecord( ref pmr, expectedLen);
                		//Debug.WriteLine( pmr.ToString());
                	}
                	else if(recType == RecordType.PLR) {
                		PLRRecord plr = new PLRRecord();
                		recReadLen = stdfStream.readRecord( ref plr, expectedLen);
                		//Debug.WriteLine( plr.ToString());
                	}
                	else if(recType == RecordType.PGR) {
                		PGRRecord pgr = new PGRRecord();
                		recReadLen = stdfStream.readRecord( ref pgr, expectedLen);
                		//Debug.WriteLine( pgr.ToString());
                		
                	}
                	else if(recType == RecordType.WIR) {
                		//WIRRecord wir = new WIRRecord();
                		recReadLen = stdfStream.readRecord( ref wir, expectedLen);
                		//Debug.WriteLine( pgr.ToString());
                		                            
                		if(foundWIR == false) {
	                		if (loadWIR)
	                        {
	                			
	                            wir.getFields(ref wirFields);
	                            /*
	                            for (int i = 0; i < wirFields.Count; i++)
	                            {
	                                //touchDownResults.Columns.Add(sdr.getRecordTypeString() + "." + sdrFields[i].First, typeof(string));
	                                addColumn(ref touchDownResults, ref data, wir.getRecordTypeString() + "." + wirFields[i].First, typeof(string));
	                                
	                            }
	                            */
	                            addColumn( ref touchDownResults, ref data, "WIR.headNumber", typeof(int));
	                            addColumn( ref touchDownResults, ref data, "WIR.siteGroup",  typeof(int));
	                            addColumn( ref touchDownResults, ref data, "WIR.startTime",  typeof(int));
	                            addColumn( ref touchDownResults, ref data, "WIR.waferID",    typeof(string));
	                            if(firstParametricColumnName != "") {
	                            	touchDownResults.Columns["WIR.headNumber"].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                            	touchDownResults.Columns["WIR.siteGroup"].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                            	touchDownResults.Columns["WIR.startTime"].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                            	touchDownResults.Columns["WIR.waferID"].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                            }
                            }
                		}
                		foundWIR = true;
                	}
                	else {
                		validRecord = false;
                	}
                }
                else if((foundMIR == true) && (foundPIR == true) && (foundSummary == false)) {
                	                	
                	if(recType == RecordType.WIR) {
                		//WIRRecord wir = new WIRRecord();
                		recReadLen = stdfStream.readRecord( ref wir, expectedLen);
                		//Debug.WriteLine( pgr.ToString());
                		                  
                		if(foundWIR == false) {
	                		if (loadWIR)
	                        {
	                			
	                            wir.getFields(ref wirFields);
	                            /*
	                            for (int i = 0; i < wirFields.Count; i++)
	                            {
	                                //touchDownResults.Columns.Add(sdr.getRecordTypeString() + "." + sdrFields[i].First, typeof(string));
	                                addColumn(ref touchDownResults, ref data, wir.getRecordTypeString() + "." + wirFields[i].First, typeof(string));
	                                
	                            }
	                            */
	                            addColumn( ref touchDownResults, ref data, "WIR.headNumber", typeof(int));
	                            addColumn( ref touchDownResults, ref data, "WIR.siteGroup",  typeof(int));
	                            addColumn( ref touchDownResults, ref data, "WIR.startTime",  typeof(int));
	                            addColumn( ref touchDownResults, ref data, "WIR.waferID",    typeof(string));
	                            if(firstParametricColumnName != "") {
	                            	touchDownResults.Columns["WIR.headNumber"].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                            	touchDownResults.Columns["WIR.siteGroup"].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                            	touchDownResults.Columns["WIR.startTime"].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                            	touchDownResults.Columns["WIR.waferID"].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                            }
                            }
                		}
                		foundWIR = true;
                	}
                	else if(recType == RecordType.PIR) {
                		PIRRecord pir = new PIRRecord();
                		recReadLen = stdfStream.readRecord( ref pir, expectedLen);
                		//Debug.WriteLine( pir.ToString());
                		
                		// Add row if necessary
						if(!siteNum2RowIndex.ContainsKey(pir.siteNumber)) {
                			DataRow tempRow = touchDownResults.NewRow();
                			siteNum2RowIndex.Add( pir.siteNumber, (touchDownResults.Rows.Count));
                			touchDownResults.Rows.Add(tempRow);
						}
                		int rowIndex = siteNum2RowIndex[pir.siteNumber];

                        // Fill Filename column value
                        touchDownResults.Rows[rowIndex]["Filename"] = fileName;

                        // Fill MIR fields column values
                        if(loadMIR == true) {
	                		for(int i=0; i < mirFields.Count; i++) {
	                			DataColumn col = touchDownResults.Columns[mir.getRecordTypeString() + "." + mirFields[i].First];
	                			touchDownResults.Rows[rowIndex][col] = mirFields[i].Second;
	                			//touchDownResults.Columns["mir." + mirFields[i].First] = mirFields[i].Second;
	                			//touchDownResults.Columns["mir." + mirFields[i].First][rowIndex] = mirFields[i].Second;
	                		}
                        }

                        // Fill SDR fields column values
                        if(loadSDR == true) {
	                        for (int i = 4; i < sdrFields.Count; i++)
	                        {
	                            DataColumn col = touchDownResults.Columns[sdr.getRecordTypeString() + "." + sdrFields[i].First];
	                            touchDownResults.Rows[rowIndex][col] = sdrFields[i].Second;
	                        }
                        }
                                                
                        // Fill WIR fields column values
                        if(foundWIR == true && loadWIR == true) {
                        	/*
	                        for (int i = 0; i < wirFields.Count; i++)
	                        {
	                            DataColumn col = touchDownResults.Columns[wir.getRecordTypeString() + "." + wirFields[i].First];
	                            touchDownResults.Rows[rowIndex][col] = wirFields[i].Second;
	                            if(firstParametricColumnName != "")
	                            	touchDownResults.Columns[col.ColumnName].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
	                        }
	                        */
	                       touchDownResults.Rows[rowIndex]["WIR.headNumber"] = wir.headNumber;
	                       touchDownResults.Rows[rowIndex]["WIR.siteGroup"]  = wir.siteGroup;
	                       touchDownResults.Rows[rowIndex]["WIR.startTime"]  = wir.startTime;
	                       touchDownResults.Rows[rowIndex]["WIR.waferID"]    = wir.waferID.ToString();
	                       
                        }
                                                	

                	}
                	else if(recType == RecordType.PTR) {
                		PTRRecord ptr = new PTRRecord();
                		recReadLen = stdfStream.readRecord( ref ptr, expectedLen);
                		//Debug.WriteLine( ptr.ToString());
                		
                		string colName = ptr.testNumber.ToString();
                		string testName = "";
                		if(ptrRegister.ContainsKey(ptr.testNumber)) {
                			testName = ptrRegister[ptr.testNumber].testText.str();
                			colName = "P" + ptr.testNumber + "_" + ptrRegister[ptr.testNumber].testText.str();
                		}
                		else {
                			testName = ptr.testText.str();
                			colName = "P" + ptr.testNumber.ToString() + "_" + ptr.testText.str();
                		}
                		
                		if(uniqueTestCount == 0 && loadPTR == true)
                			firstParametricColumnName = colName;

                        if (ptrRegister.ContainsKey(ptr.testNumber) == false)
                        {
                            ptrRegister.Add(ptr.testNumber, ptr);
                            uniqueTestCount++;
                        }
                        if (loadPTR)
                        {
                            if (touchDownResults.Columns.Contains(colName) == false)
                            {
                                //touchDownResults.Columns.Add( colName, typeof(double));
                                addColumn(ref touchDownResults, ref data, colName, typeof(double));

                                touchDownResults.Columns[colName].ExtendedProperties.Add("Info.FIELDTYPE", "Parametric");
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.TESTNUMBER", (Double)ptr.testNumber);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.TESTNAME", testName);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.PARMTYPE", "Real");

                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.LPL", (Double)ptr.loLimit);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.UPL", (Double)ptr.hiLimit);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.UNIT", ptr.units.str());
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.SCALEFACTOR", (int)ptr.resultScale);
                                //touchDownResults.Columns[colName].ExtendedProperties.Add( "Test.LoLimitScale",  ptr.llmScale);
                                //touchDownResults.Columns[colName].ExtendedProperties.Add( "Test.HiLimitScale",  ptr.hlmScale);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.LSL", (Double)ptr.loSpec);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.USL", (Double)ptr.hiSpec);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.LOL", (Double)0.0);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.UOL", (Double)0.0);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.LWL", (Double)0.0);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.UWL", (Double)0.0);
                                string limValid = "N";
                                if (ptr.isHiLimitInvalid() & !ptr.isLoLimitInvalid())
                                    limValid = "L";
                                else if (!ptr.isHiLimitInvalid() & ptr.isLoLimitInvalid())
                                    limValid = "H";
                                else if (!ptr.isHiLimitInvalid() & !ptr.isLoLimitInvalid())
                                    limValid = "B";

                                touchDownResults.Columns[colName].ExtendedProperties.Add("Limit.VALID", limValid);

                            }
                            bool considerResult = true;

                            if (ignoreAlarm)
                                considerResult = ptr.considerResultWithoutAlarm();
                            else
                                considerResult = ptr.considerResult();
                            
                            if (considerResult)
                            {
                                if (!siteNum2RowIndex.ContainsKey(ptr.siteNumber))
                                {
                                    DataRow tempRow = touchDownResults.NewRow();
                                    siteNum2RowIndex.Add(ptr.siteNumber, touchDownResults.Rows.Count);
                                    touchDownResults.Rows.Add(tempRow);
                                }

                                int rowIndex = siteNum2RowIndex[ptr.siteNumber];

                                if (rowIndex >= 0)
                                    touchDownResults.Rows[rowIndex][colName] = ptr.testResult;

                            }
                        }
                	}
                        
                    else if (recType == RecordType.FTR)
                    {
                        FTRRecord ftr = new FTRRecord();
                        recReadLen = stdfStream.readRecord(ref ftr, expectedLen);
                        //Debug.WriteLine( ftr.ToString());

                        string colName = ftr.testNumber.ToString();
                        string testName = "";
                        if (ftrRegister.ContainsKey(ftr.testNumber))
                        {
                            testName = ftrRegister[ftr.testNumber].testText.str();
                            colName = "F" + ftr.testNumber + "_" + ftrRegister[ftr.testNumber].testText.str();
                        }
                        else
                        {
                            testName = ftr.testText.str();
                            colName = "F" + ftr.testNumber.ToString() + "_" + ftr.testText.str();
                        }

                        if (uniqueTestCount == 0 && loadFTR == true)
                            firstFunctionalColumnName = colName;

                        if (ftrRegister.ContainsKey(ftr.testNumber) == false)
                        {
                            ftrRegister.Add(ftr.testNumber, ftr);
                            uniqueTestCount++;
                        }
                        if (loadFTR)
                        {
                            if (touchDownResults.Columns.Contains(colName) == false)
                            {
                                //touchDownResults.Columns.Add( colName, typeof(double));
                                addColumn(ref touchDownResults, ref data, colName, typeof(int));

                                touchDownResults.Columns[colName].ExtendedProperties.Add("Info.FIELDTYPE", "Functional");
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.TESTNUMBER", (Double)ftr.testNumber);
                                touchDownResults.Columns[colName].ExtendedProperties.Add("Test.TESTNAME", testName);
                            }
                            if (ftr.isPFValid())
                            {
                                if (!siteNum2RowIndex.ContainsKey(ftr.siteNumber))
                                {
                                    DataRow tempRow = touchDownResults.NewRow();
                                    siteNum2RowIndex.Add(ftr.siteNumber, touchDownResults.Rows.Count);
                                    touchDownResults.Rows.Add(tempRow);
                                }

                                int rowIndex = siteNum2RowIndex[ftr.siteNumber];

                                if (rowIndex >= 0)
                                {
                                    if (ftr.isPass())
                                        touchDownResults.Rows[rowIndex][colName] = 1;
                                    else
                                        touchDownResults.Rows[rowIndex][colName] = 0;
                                }

                            }
                        }
                    }
                    
                    else if (recType == RecordType.PRR)
                    {
                        PRRRecord prr = new PRRRecord();
                        recReadLen = stdfStream.readRecord(ref prr, expectedLen);
                        //Debug.WriteLine( prr.ToString());

                        List<Pair<string, string>> prrFields = new List<Pair<string, string>>();
                        prr.getFields(ref prrFields);
                        // Add columns if this is the first time
              
                        if(!touchDownResults.Columns.Contains("PRR.headNumber")) {
//                        	addColumn( ref touchDownResults, ref data, "PRR.headNumber", prr.headNumber.GetType());
//                        	addColumn( ref touchDownResults, ref data, "PRR.siteNumber", prr.siteNumber.GetType());
//                        	addColumn( ref touchDownResults, ref data, "PRR.partFlag",   prr.partFlag.GetType());
//                        	addColumn( ref touchDownResults, ref data, "PRR.numTests",   prr.numTests.GetType());
//                        	addColumn( ref touchDownResults, ref data, "PRR.hardBin",    prr.hardBin.GetType());   
//                        	addColumn( ref touchDownResults, ref data, "PRR.softBin",    prr.softBin.GetType());
//                        	addColumn( ref touchDownResults, ref data, "PRR.xCoord",     prr.xCoord.GetType());
//                        	addColumn( ref touchDownResults, ref data, "PRR.yCoord",     prr.yCoord.GetType());
//                        	addColumn( ref touchDownResults, ref data, "PRR.testTime",   prr.testTime.GetType());   
//                        	addColumn( ref touchDownResults, ref data, "PRR.partID",     prr.partID.GetType());                        	
//                        	addColumn( ref touchDownResults, ref data, "PRR.partText",   prr.partText.GetType());        
//                        	addColumn( ref touchDownResults, ref data, "PRR.partFix",    prr.partFix.GetType());   
                        	
							addColumn( ref touchDownResults, ref data, "PRR.headNumber", typeof(int));
							addColumn( ref touchDownResults, ref data, "PRR.siteNumber", typeof(int));
                        	addColumn( ref touchDownResults, ref data, "PRR.partFlag",   typeof(int));
                        	addColumn( ref touchDownResults, ref data, "PRR.numTests",   typeof(int));
                        	addColumn( ref touchDownResults, ref data, "PRR.hardBin",    typeof(int));   
                        	addColumn( ref touchDownResults, ref data, "PRR.softBin",    typeof(int));
                        	addColumn( ref touchDownResults, ref data, "PRR.xCoord",     typeof(int));
                        	addColumn( ref touchDownResults, ref data, "PRR.yCoord",     typeof(int));
                        	addColumn( ref touchDownResults, ref data, "PRR.testTime",   typeof(int));   
                        	addColumn( ref touchDownResults, ref data, "PRR.partID",     typeof(string));                        	
                        	addColumn( ref touchDownResults, ref data, "PRR.partText",   typeof(string));        
                        	addColumn( ref touchDownResults, ref data, "PRR.partFix",    typeof(string));   
                        	if (firstParametricColumnName != "") {
                        		for (int i = 0; i < prrFields.Count; i++)
                        			touchDownResults.Columns[prr.getRecordTypeString() + "." + prrFields[i].First].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
                        		
                        	}
                        }
                        /*
                        for (int i = 0; i < prrFields.Count; i++)
                        {
                            string colName = prr.getRecordTypeString() + "." + prrFields[i].First;
                            if (touchDownResults.Columns.Contains(colName) == false)
                            {
                                //touchDownResults.Columns.Add( colName, typeof(string));
                                //if(prrFields[i].First 
                                addColumn(ref touchDownResults, ref data, colName, typeof(string));
                                // Adjust the column location to be just before the parametric results
                                if (firstParametricColumnName != "")
                                {
                                    touchDownResults.Columns[colName].SetOrdinal(touchDownResults.Columns.IndexOf(firstParametricColumnName));
                                }
                            }
                        }
                        */

                        if (prr.siteNumber >= 0 && prr.siteNumber < 255)
                        {
                            uniquePartNumber++;

                            // Add PRR data to table
                            if (siteNum2RowIndex.ContainsKey(prr.siteNumber))
                            {
                                int rowIndex = siteNum2RowIndex[prr.siteNumber];
                                touchDownResults.Rows[rowIndex]["PRR.headNumber"] = prr.headNumber;
                                touchDownResults.Rows[rowIndex]["PRR.siteNumber"] = prr.siteNumber;    
                                touchDownResults.Rows[rowIndex]["PRR.partFlag"] = prr.partFlag;
                                touchDownResults.Rows[rowIndex]["PRR.numTests"] = prr.numTests;                                
                                touchDownResults.Rows[rowIndex]["PRR.hardBin"] = prr.hardBin;                               
                                touchDownResults.Rows[rowIndex]["PRR.softBin"] = prr.softBin;                              
                                touchDownResults.Rows[rowIndex]["PRR.xCoord"] = prr.xCoord;   
                                touchDownResults.Rows[rowIndex]["PRR.yCoord"] = prr.yCoord;       
                                touchDownResults.Rows[rowIndex]["PRR.testTime"] = prr.testTime; 
                                touchDownResults.Rows[rowIndex]["PRR.partID"] = prr.partID.ToString();
                                touchDownResults.Rows[rowIndex]["PRR.partText"] = prr.partText.ToString();
                                touchDownResults.Rows[rowIndex]["PRR.partFix"] = prr.partFix.ToString();
                                /*
                                for (int i = 0; i < prrFields.Count; i++)
                                {
                                    string colName = prr.getRecordTypeString() + "." + prrFields[i].First;
                                    touchDownResults.Rows[rowIndex][colName] = prrFields[i].Second;
                                }
                                */
                            }
                            // Copy this units data to Table "data"
                            if (siteNum2RowIndex.ContainsKey(prr.siteNumber))
                            {
                                int rowIndex = siteNum2RowIndex[prr.siteNumber];
                                DataRow tempRow = touchDownResults.Rows[rowIndex];
                                if (uniquePartNumber == 1)
                                    data = touchDownResults.Clone();
                                data.ImportRow(tempRow);

                                Dictionary<U1, int> updatedSiteNum2RowIndex = new Dictionary<U1, int>();
                                foreach (KeyValuePair<U1, int> pair in siteNum2RowIndex)
                                {
                                    if (pair.Value < rowIndex)
                                        updatedSiteNum2RowIndex[pair.Key] = pair.Value;
                                    else if (pair.Value > rowIndex)
                                        updatedSiteNum2RowIndex[pair.Key] = pair.Value - 1;
                                    else if (pair.Value == rowIndex)
                                        ;
                                }
                                touchDownResults.Rows.RemoveAt(rowIndex);
                                siteNum2RowIndex.Remove(prr.siteNumber);
                                siteNum2RowIndex = updatedSiteNum2RowIndex;
                                //updatedSiteNum2RowIndex.Clear();
                            }

                        }
                    }
                    else
                    {
                        validRecord = false;
                    }
                	
                }
                else if(foundSummary == true) {
                        validRecord = false;
                }
                else {
                	validRecord = false;
                }
                
                if(validRecord == false) {
                	recReadLen = (int) stdfStream.skip((U2) expectedLen);
                	validRecord = true; // clear flag
                	result = false;
                }
            }

            //
            // Iterate through all the columns and create a dictionary of all column properties
            // This is required because each column in DataTable can have its own list of Properties in PropertyCollection
            //
            Dictionary<string, Type> propToType = new Dictionary<string, Type>();
            foreach (DataColumn col in touchDownResults.Columns)
            {
                PropertyCollection pColl = col.ExtendedProperties;

                foreach (string name in pColl.Keys)
                {
                	//if(!data.Columns[col.ColumnName].ExtendedProperties.Contains(name))
                	data.Columns[col.ColumnName].ExtendedProperties[name] = pColl[name];
                    if (!propToType.ContainsKey(name))
                    {
                        propToType.Add(name, pColl[name].GetType());
                    }
                }
            }
            //
            // Now create properties for columns that may not have them
            //
            foreach (DataColumn col in touchDownResults.Columns)
            {
                PropertyCollection pColl = col.ExtendedProperties;
                foreach (string name in propToType.Keys)
                {
                    if (!pColl.ContainsKey(name))
                    {
                        pColl.Add(name, propToType[name]);
                    }
                }
            }
            //
            // Fill external table
            //
            DataTable extPropTable = new DataTable();
            extPropTable.Columns.Add("Name", typeof(string));
            foreach (DataColumn col in touchDownResults.Columns)
            {
                extPropTable.Columns.Add(col.ColumnName, typeof(string));
            }
            foreach (string name in propToType.Keys)
            {
                //extPropTable.Columns.Add(name, propToType[name]);
                DataRow tempRow = extPropTable.NewRow();
                tempRow["Name"] = name;
                extPropTable.Rows.Add(tempRow);
            }
            foreach (DataColumn col in touchDownResults.Columns)
            {
                PropertyCollection pColl = col.ExtendedProperties;
                int i = 0;
                foreach (string name in pColl.Keys)
                {
                    extPropTable.Rows[i++][col.ColumnName] = pColl[name];
                }
            }

            headerData = extPropTable;

            return result;
        }

        public virtual bool close() {
            bool success = false;
            // TODO

            return success;
        }
		
		//
		// Members
		//
		protected string fileName;
		protected RWMode rwMode;
		protected Stream stdfStream;
	}
	
}
